/*  ****************************************************************
 *  ConfigFile.cpp
 *  CheeseBurgerParadise
 *
 *  Config file parser
 * *****************************************************************
 */

#include <ogreplatform.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <Carbon/Carbon.h>
#endif

#include <sstream>
#include "ConfigFile.h"

using namespace std;
using namespace Ogre;

// initialize static member varaibles
ifstream BandConfigFile::input;
list<CharacterConfig> BandConfigFile::allCharacters;
string BandConfigFile::soundtrackFile("");
bool BandConfigFile::loadBand = true;
string BandConfigFile::mocapPath("");
string BandConfigFile::modelsPath("");

bool BandConfigFile::openConfigFile(const char * filename)
{
	input.open(filename);
	return input.is_open();
}

bool BandConfigFile::openDefaultConfigFile()
{

	return BandConfigFile::openConfigFile(getDefaultConfigFile().c_str());	
}

string BandConfigFile::getDefaultConfigFile()
{
	// determine path of default band configuration file
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

	const char * bandConfigFile = "band.cfg";
#elif OGRE_PLATFORM == OGRE_PLATFORM_APPLE
			
	// get .app bundle 
	CFBundleRef mainBundle = CFBundleGetMainBundle();
			
	// get band.cfg URL
	CFURLRef	bandConfigURL = CFBundleCopyResourceURL( mainBundle, CFSTR("band"), CFSTR("cfg"), NULL);
			
	// convert to POSIX path
	CFStringRef bandConfigStr = CFURLCopyFileSystemPath(bandConfigURL, kCFURLPOSIXPathStyle);
			
	// convert to C-str
	char bandConfigFile[1024*4];
	CFStringGetCString(bandConfigStr, bandConfigFile, 1024*4, kCFStringEncodingASCII);
#endif

	return string(bandConfigFile);
}


bool BandConfigFile::reload()
{
	return openDefaultConfigFile() && parse();
}

bool BandConfigFile::parse()
{
	if (!input.is_open()) {
		return false;
	}
	
	allCharacters.clear();
	bool characterOk = true;
	bool firstCharacter = true;
	
	// character attributes
	string name, mesh, vsk, v, map; Vector3 offset(0.0f, 0.0f, 0.0f);
	string instrumentV, instrumentVSK, instrumentMesh, instrumentMap; Vector3 instrumentOffset(0.0f, 0.0f, 0.0f);
	float deltaTime = 0.0f;
	
	while (!input.eof()) {
	
		string line;
		string attribute, data;
		
		// read one line from file
		getline(input, line);
		
		// skip empty, whitespace, and comment (starting with #) lines
		if (0 == line.size() || '#' == line[0] || isWhite(line)) {
			continue;
		}
		
		
		if (0 == stricmp(line.c_str(), "[character]")) {
			// new character
			
			// did we see all attributes for previous character?
			if (!firstCharacter) {
				
				if (characterOk) {
					
					// add previous character
					CharacterConfig newCharacter(name, mesh, vsk, v, map, offset, deltaTime);
					newCharacter.setInstrumentV(instrumentV);
					newCharacter.setInstrumentVSK(instrumentVSK);
					newCharacter.setInstrumentOffset(instrumentOffset);
					newCharacter.setInstrumentMesh(instrumentMesh);
					newCharacter.setInstrumentMap(instrumentMap);
					
					allCharacters.push_back(newCharacter);
				
					// reset flags
					characterOk = true;
					mesh = vsk = v = map = name = instrumentMesh = instrumentV = instrumentVSK = instrumentMap = string("");
					offset = Vector3(0.0f, 0.0f, 0.0f);
					deltaTime = 0.0f;

				}
				else {
					// previous charcter info not yet complete
					input.close();
					return false;
				}
			}
			
			firstCharacter = false;
			characterOk = false;
			
		}
		else
		{
			//  attribute = data
			size_t sign = line.find('=');
			if (string::npos == sign) {
				// parse error
				input.close();
				return false;
			}
			
			attribute = line.substr(0, sign);
			data = line.substr(sign + 1);
			
			if (attribute.length() == 0 && data.length() == 0) {
				// parse error
				input.close();
				return false;
			}
			
			if (0 == stricmp(attribute.c_str(), "mesh")) {
				mesh = data;
			} else if (0 == stricmp(attribute.c_str(), "vsk")) {
				vsk = data;
			} else if (0 == stricmp(attribute.c_str(), "v")) {
				v = data;
			} else if (0 == stricmp(attribute.c_str(), "map")) {
				map = data;
			} else if (0 == stricmp(attribute.c_str(), "offset")) {
				bool status;
				offset = readOffsetVector(data, status);
				if (!status) {
					// parse error
					input.close();
					return false;
				}
			}
			else if (0 == stricmp(attribute.c_str(), "instrumentoffset")) {
				bool status;
				instrumentOffset = readOffsetVector(data, status);
				if (!status) {
					// parse error
					input.close();
					return false;
				}
			}
			else if (0 == stricmp(attribute.c_str(), "name")) {
				name = data;
			}
			else if (0 == stricmp(attribute.c_str(), "instrumentv")) {
				instrumentV = data;
			}
			else if (0 == stricmp(attribute.c_str(), "instrumentvsk")) {
				instrumentVSK = data;
			}
			else if (0 == stricmp(attribute.c_str(), "instrumentmesh")) {
				instrumentMesh = data;
			}
			else if (0 == stricmp(attribute.c_str(), "instrumentmap")) {
				instrumentMap = data;
			}
			else if (0 == stricmp(attribute.c_str(), "deltatime")) {
				deltaTime = readFloat(data);
			}
			else if (0 == stricmp(attribute.c_str(), "soundtrack")) {
				soundtrackFile = data;
			}
			else if (0 == stricmp(attribute.c_str(), "mocappath")) {
				mocapPath = data;
			}
			else if (0 == stricmp(attribute.c_str(), "modelspath")) {
				modelsPath = data;
			}
			else if (0 == stricmp(attribute.c_str(), "loadband")) {
				if (data == "1") {
					loadBand = true;
				} else {
					loadBand = false;
				}
			}
			else
			{
				input.close();
				return false;
			}
			
			if (name.length() > 0 && mesh.length() > 0 && vsk.length() > 0 && v.length() > 0 && map.length() > 0) {
				
				characterOk = true;
		
			}
		}
	}
	
	// add last character
	if (!firstCharacter) {
		if (characterOk) {
					
			// add previous character
			CharacterConfig newCharacter(name, mesh, vsk, v, map, offset, deltaTime);
			newCharacter.setInstrumentV(instrumentV);
			newCharacter.setInstrumentVSK(instrumentVSK);
			newCharacter.setInstrumentOffset(instrumentOffset);
			newCharacter.setInstrumentMesh(instrumentMesh);
			newCharacter.setInstrumentMap(instrumentMap);
			
			allCharacters.push_back(newCharacter);

		}
		else {
			// previous charcter info not yet complete
			input.close();
			return false;
		}
	}
	
	input.close();
	return true;
}


bool isWhite(const string & str)
{
	for (unsigned int i = 0; i < str.length(); i++) {
		if (str[i] != ' ' && str[i] != '\t') {
			return false;
		}
	}
	
	return true;
}

Vector3 readOffsetVector(const string & data, bool & status)
{
	status = true;
	float x;
	float y;
	float z;
	
	// convert string to stream
	stringstream buffer(stringstream::in | stringstream::out);
	buffer << data;
	buffer >> x >> y >> z;
	
	return Vector3(x, y, z);
}

float readFloat(const string & data)
{
	float x;
	
	// convert string to stream
	stringstream buffer(stringstream::in | stringstream::out);
	buffer << data;
	buffer >> x;
	
	return x;
}

void printBandConfig()
{
	const CharacterList & characters = BandConfigFile::getCharacters();
	
	for (CharacterList::const_iterator it = characters.begin();
		it != characters.end(); it++) {
		
		const CharacterConfig & character = *it;
		Vector3 offset = character.getOffset();
		
		cout << "Character" << endl;
		cout << "\tName: " << character.getName() << endl;
		cout << "\tMesh: " << character.getMeshFile() << endl;
		cout << "\tVSK: " << character.getVSKFile() << endl;
		cout << "\tV: " << character.getVFile() << endl;
		cout << "\tMap: " << character.getMapFile() << endl;
		
		if (character.hasInstrument()) {
			cout << "\tHas instrument: " << endl;
			cout << "\t\tInstrument Mesh: " << character.getInstrumentMesh() << endl;
			cout << "\t\tInstrument V: " << character.getInstrumentV() << endl;
			cout << "\t\tInstrument VSK: " << character.getInstrumentVSK() << endl;
			cout << "\t\tInstrument MAP: " << character.getInstrumentMap() << endl;
		} else {
			cout << "\tDoesn't have instrument.\n";
		}
		
		cout << "\tOffset: " << offset[0] << ", " << offset[1] << ", " << offset[2] << endl;
	}
}
